package org.systemsbiology.addama.sequencing.controllers;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.multipart.support.DefaultMultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.mail.SimpleMailMessage;
import org.json.JSONObject;
import org.json.JSONException;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.protocol.Protocol;
import org.systemsbiology.addama.sequencing.util.CGSChromRefMap;
import org.systemsbiology.addama.sequencing.dao.StrainRadtag;
import org.systemsbiology.addama.commons.httpclient.support.*;
import org.systemsbiology.addama.commons.httpclient.support.ssl.EasySSLProtocolSocketFactory;
import org.systemsbiology.addama.commons.web.views.JsonView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.ServletContext;
import java.util.*;
import java.io.*;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.ResultSet;

/**
 * Created by IntelliJ IDEA.
 * User: jlin
 * Date: Aug 27, 2010
 * Time: 4:37:40 PM
 * To change this template use File | Settings | File Templates.
 */
public class CompleteGenomeSeqController extends LIMSAbstractController implements ServletContextAware {

    protected final Logger log = LoggerFactory.getLogger(getClass());
    private String seqRepoName;
    private String cgsScriptUser;
    private String cgsScriptPassword;
    private final Map<String, MultipartFile> filesByName = new HashMap<String, MultipartFile>();
    public static String WEBAPP_ROOT;
    public static StringBuffer msgBuffer = new StringBuffer();
    ServletContext servletContext = null;
    private Credentials credentials;
    private HttpClientTemplate httpClientTemplate;
    private HttpClient httpClient;
    private CommonsMultipartResolver multipartResolver;

    public void setMultipartResolver(CommonsMultipartResolver multipartResolver) {
        this.multipartResolver = multipartResolver;
    }

    protected JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public void setCredentials(Credentials credentials) {
        this.credentials = credentials;
    }

    public void setHttpClientTemplate(HttpClientTemplate httpClientTemplate) {
        this.httpClientTemplate = httpClientTemplate;
    }

    public String getCgsScriptUser() {
        return cgsScriptUser;
    }

    public void setCgsScriptUser(String cgsScriptUser) {
        this.cgsScriptUser = cgsScriptUser;
    }

    public String getCgsScriptPassword() {
        return cgsScriptPassword;
    }

    public void setCgsScriptPassword(String cgsScriptPassword) {
        this.cgsScriptPassword = cgsScriptPassword;
    }

    public String getSeqRepoName() {
        return seqRepoName;
    }

    public void setSeqRepoName(String seqRepoName) {
        this.seqRepoName = seqRepoName;
    }

    @Override
    public void setServletContext(ServletContext servletContext) {
        //To change body of implemented methods use File | Settings | File Templates.
        this.servletContext = servletContext;
    }

    /*
     * Dependency Injection
     */
    public void setHttpClient(HttpClient httpClient) {
        this.httpClient = httpClient;
    }

    /*
    * Method Implementations: InitializingBean
    */

    public void afterPropertiesSet() throws Exception {
        this.httpClient.getState().setCredentials(AuthScope.ANY, this.credentials);

    }

    @RequestMapping(value = "/**/translategenename", method = RequestMethod.GET)
    @ModelAttribute
    public ModelAndView getSystematicGeneName(HttpServletRequest request, NativeWebRequest nativeWebRequest) throws Exception {
        ModelAndView mav = new ModelAndView(new JsonView());
        log.info("\nBegin GET getSystematicGeneName");
        try {
            JSONObject jsonObject = new JSONObject();
            Map reqParams = request.getParameterMap();
            for (Object keyObj : reqParams.keySet()) {
                String key = (String) keyObj;
                for (String value : request.getParameterValues(key)) {
                    jsonObject.put(key, value);
                }
            }
            String geneInput = jsonObject.getString("genename").toUpperCase();
            String systematicName = (String) this.jdbcTemplate.queryForObject("select systematic_name from yeast_gene_annotations where common_name = ? or systematic_name = ?", new Object[]{geneInput, geneInput}, String.class);
            jsonObject.put("systematicname", systematicName);
            mav.addObject("json", jsonObject);
            log.info("\nDone GET getSystematicGeneName");
            return mav;
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }


    @RequestMapping(value = "/**/radtag2bc", method = RequestMethod.GET)
    @ModelAttribute
    public ModelAndView getStrain2Ragtag(HttpServletRequest request, NativeWebRequest nativeWebRequest) throws Exception {
        return processStrainRagtag(request, nativeWebRequest);
    }

    @RequestMapping(value = "/**/radtag2bc", method = RequestMethod.POST)
    @ModelAttribute
    public ModelAndView processStrainRagtag(HttpServletRequest request, NativeWebRequest nativeWebRequest) throws Exception {
        ModelAndView mav = new ModelAndView(new JsonView());
        log.info("\nBegin GET processStrainRagtag");
        try {
            JSONObject jsonObject = new JSONObject();
            Map reqParams = request.getParameterMap();
            for (Object keyObj : reqParams.keySet()) {
                String key = (String) keyObj;
                for (String value : request.getParameterValues(key)) {
                    log.info("CGS Script WS parameters -> key: " + key + " value: " + value);
                    jsonObject.put(key, value);
                }
            }
            if (jsonObject.getString("user").equals(this.getCgsScriptUser()) && jsonObject.getString("password").equals(this.getCgsScriptPassword())) {
                Collection set = getStrainRadtagBc(jsonObject.getString("seqkey"));
                Iterator it = set.iterator();
                boolean first = true;
                while (it.hasNext()) {
                    StrainRadtag tag = (StrainRadtag) it.next();
                    if (first) {
                        jsonObject.put("flowcell", tag.getFlowcell_lane());
                        first = false;
                    }
                    jsonObject.put(tag.getStrainKey(), tag.getRadtagBc());
                }
            } else {
                jsonObject.put("status", "Unauthorized");
                jsonObject.put("comments", "Check with Gareth/Jake about accesing API");
                mav.addObject("json", jsonObject);
                return mav;
            }
            mav.addObject("json", jsonObject);
            log.info("\nDone GET processStrainRagtag");

            return mav;
        } catch (Exception e) {
            e.printStackTrace();
            this.sendNotification("LIMS Notification:CGS Created Failed radtag2BC:", "Exception \n" + e + " \n\nWarning messages:\n" + msgBuffer.toString());
            throw e;
        }
    }


    @RequestMapping(value = "/**/submit", method = RequestMethod.POST)
    @ModelAttribute
    public ModelAndView processCGSSubmission(HttpServletRequest request, NativeWebRequest nativeWebRequest) throws Exception {
        int nextSeqNum = -1;
        ModelAndView mav = new ModelAndView(new JsonView());
        log.info("\nBegin processCGSSubmission");

        try {
            JSONObject jsonObject = new JSONObject();
            Map reqParams = request.getParameterMap();
            for (Object keyObj : reqParams.keySet()) {
                String key = (String) keyObj;
                for (String value : request.getParameterValues(key)) {
                    log.info("CGS parameters -> key: " + key + " value: " + value);
                    jsonObject.put(key, value);
                }
            }

            this.addMultipartParam(jsonObject, nativeWebRequest);
            //jsonObject.put("YCGSCoverage", parseCoverageSummary());
            //jsonObject.put("YCGSMethods", "methods labels");
            //jsonObject.put("YCGSCoverageArray", getJsonArray());

            //need to figure out the YCGSKey if not passed in (in case where the request is coming from outside LIMS
            //check username and password to ensure that it is coming from predefined handshakes
            if (!jsonObject.has("YCGSKey")) {
                if (jsonObject.getString("user").equals(this.getCgsScriptUser()) && jsonObject.getString("password").equals(this.getCgsScriptPassword())) {
                    int nextKey = this.getNextCGSEntry(request);
                    jsonObject.put("YCGSKey", "YCGS" + nextKey);
                    this.getFlowcellInfo(request, jsonObject);
                    jsonObject.put("uri", "/addama-rest/dudley-sample/path/datasource/YCGS/" + nextKey);
                    //set IGV flag on in cases of submits from Gareth
                    jsonObject.put("IGV", "on");
                } else {
                    jsonObject.put("status", "Unauthorized");
                    jsonObject.put("comments", "Check with Gareth/Jake about accesing API");
                    mav.addObject("json", jsonObject);
                    return mav;
                }
            }

            String cgsKey = jsonObject.getString("YCGSKey");
            final String[] seqNameArray = cgsKey.split("CGS");
            nextSeqNum = Integer.parseInt(seqNameArray[1]);
            final String keyUri = "/addama-rest/dudley-sample/path/datasource/YCGS/YCGS" + seqNameArray[1];
            processCGSJCRCreate(request, nativeWebRequest, jsonObject, keyUri);
            processCGSJCRAnnotation(request, nextSeqNum);
            //update ploidy determination
            if (!jsonObject.getString("ploidy").equals("")) {
                log.info("Update YO " + jsonObject.getString("YCGSStrain") + " ploidy: and ploidy notes, determination method:" + jsonObject.getString("ploidyNotes"));
                this.reportPloidy(request, jsonObject);
            }
            processIGVLabspaceMutation(request, jsonObject, cgsKey);
            processIGVLabspaceBam(request, jsonObject, cgsKey);
            processCGSOmicsView("YCGS" + nextSeqNum, "CGS/SNP", jsonObject.getString("YCGSStrain"), jsonObject.getString("YCGSDudleyProject"), "submitted", jsonObject.getString("YCGSSeqRun"), "", "INSERT");
            this.sendNotification("LIMS Notification:YCGS" + nextSeqNum + " Created", "Submitted CGS:\n" + jsonObject + " \nWarning messages:\n" + msgBuffer.toString());
            log.info("End processCGSSubmission");
            JSONObject json = new JSONObject();
            json.put("YCGSKey", cgsKey);
            json.put("uri", keyUri);
            json.put("status", "Completed");
            mav.addObject("json", json);
            return mav;
        } catch (Exception e) {
            e.printStackTrace();
            this.sendNotification("LIMS Notification:CGS Submit Created Failed:YCGS" + nextSeqNum, "Exception \n" + e + " \n\nWarning messages:\n" + msgBuffer.toString());
            throw e;
        }
    }

    @RequestMapping(value = "/**/mergesnp", method = RequestMethod.POST)
    @ModelAttribute
    public ModelAndView processMergeSnp(HttpServletRequest request, NativeWebRequest nativeWebRequest) throws Exception {
        String crossKey = "";
        ModelAndView mav = new ModelAndView(new JsonView());
        log.info("\nBegin processMergeSnp");
        try {
            JSONObject jsonObject = new JSONObject();
            Map reqParams = request.getParameterMap();
            for (Object keyObj : reqParams.keySet()) {
                String key = (String) keyObj;
                for (String value : request.getParameterValues(key)) {
                    log.info("CGS parameters -> key: " + key + " value: " + value);
                    jsonObject.put(key, value);
                }
            }

            if (!(jsonObject.getString("user").equals(this.getCgsScriptUser()) && jsonObject.getString("password").equals(this.getCgsScriptPassword()))) {
                jsonObject.put("status", "Unauthorized");
                jsonObject.put("comments", "Check with Gareth/Jake about accesing API");
                mav.addObject("json", jsonObject);
                return mav;
            }
            jsonObject.put("IGV", "on");
            crossKey = jsonObject.getString("YCR");

            /*String cgsKey1 = jsonObject.getString("YCGSKey1");
            String cgsKey2 = jsonObject.getString("YCGSKey2");
            String snpDupes = jsonObject.getString("SNPDupes");
            String snps = jsonObject.getString("SNPs");
            */
            final String keyUri = "/addama-rest/dudley-sample/path/datasource/YCR/" + crossKey.replaceAll("YCR", "YCR%20");
            processCGSYCRAnnotation(request, jsonObject, keyUri);
            processIGVLabspaceCrossMutation(request, jsonObject);
            this.sendNotification("LIMS Notification:YCR SNPs Merged", "Update YCR:\n" + jsonObject + " \nWarning messages:\n" + msgBuffer.toString());
            log.info("End processSNPMerge");
            JSONObject json = new JSONObject();
            //json.put("YCGSKey", cgsKey);
            json.put("uri", keyUri);
            json.put("status", "Completed");
            mav.addObject("json", json);
            return mav;
        } catch (Exception e) {
            e.printStackTrace();
            this.sendNotification("LIMS Notification:YCR SNPs Merging Failed " + crossKey, "Exception \n" + e + " \n\nWarning messages:\n" + msgBuffer.toString());
            throw e;
        }
    }

    private int getNextCGSEntry(HttpServletRequest request) {
        log.info("Beginning of getNextCGSEntry");
        final String url = "/addama-rest/dudley-sample/path/datasource/YCGS/annotations";
        GetMethod addamaGet = new GetMethod(getBaseUrl(request) + url);
        try {
            if (this.login(getHttpsUrl(request))) {
                addamaGet.setRequestHeader("Accept", "application/json");
                JSONObject jsonRespObj = (JSONObject) this.httpClientTemplate.executeMethod(addamaGet, new JsonResponseCallback());
                return (jsonRespObj.getInt("YCGSMaxNum") + 1);
            } else {
                log.error("Failed Authentication");
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("Error exception:" + e + "\nURL:" + url);
        }
        finally {
            addamaGet.releaseConnection();
            log.info("released connection for url " + url);
        }
        log.info("End of getNextCGSEntry");
        return -1;
    }

    private void getFlowcellInfo(HttpServletRequest request, JSONObject jsonObj) {
        log.info("Beginning of getFlowcellInfo");
        GetMethod addamaGet = null;
        try {
            String run = jsonObj.getString("YCGSSeqRun");
            String strain = jsonObj.getString("YCGSStrain").replaceAll("YO", "YO_");
            final String url = "/addama-rest/dudley-sample/path/datasource/YSEQ/" + run + "/" + strain + "/annotations";
            addamaGet = new GetMethod(getBaseUrl(request) + url);
            if (this.login(getHttpsUrl(request))) {
                addamaGet.setRequestHeader("Accept", "application/json");
                JSONObject jsonRespObj = (JSONObject) this.httpClientTemplate.executeMethod(addamaGet, new JsonResponseCallback());
                jsonObj.put("YCGSFlowcell", jsonRespObj.getString("flowcell"));
                jsonObj.put("YCGSLane", jsonRespObj.getString("lane"));
            } else {
                log.error("Failed Authentication");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            addamaGet.releaseConnection();
        }
        log.info("End of getFlowcellInfo");
    }

    private void processCGSJCRCreate(HttpServletRequest request, NativeWebRequest nativeWebRequest, JSONObject jsonObj, String cgsUri) throws Exception {
        String cgsKey = jsonObj.getString("YCGSKey");
        String seqKey = jsonObj.getString("YCGSSeqRun");
        String sample = jsonObj.getString("YCGSStrain");
        String fc = jsonObj.getString("YCGSFlowcell");
        String lane = jsonObj.getString("YCGSLane");
        final String url = getBaseUrl(request) + cgsUri + "/create";
        log.info("Begin processCGSJCRCreate (" + url + ")," + new Date() + " sequencing batch  )");
        PostMethod postCreate = new PostMethod(url);
        try {
            if (this.login(getHttpsUrl(request))) {
                postCreate.setQueryString(new NameValuePair[]{new NameValuePair("JSON", jsonObj.toString())});
                forwardAttachments(nativeWebRequest, postCreate, cgsKey, seqKey, sample, fc, lane);
                this.httpClientTemplate.executeMethod(postCreate, new ResponseCallback() {
                    public Object onResponse(int status, HttpMethod httpMethod) throws HttpClientResponseException {
                        if (status != 200) {
                            throw new HttpClientResponseException(status, httpMethod, null);
                        } else {
                            log.info("CREATE " + url + " successful");
                            return null;
                        }
                    }
                }
                );
            } else {
                log.error("Failed Authentication");
            }
        } catch (Exception e) {
            e.printStackTrace();
            this.sendNotification("LIMS Notification:Create Status Error:" + url, "Create error while processing " + url + "\nExeception\n" + e + "\nJake will be looking into it.");
            throw e;
        }
        finally {
            postCreate.releaseConnection();
            log.info("released connection for url " + url);
        }
        log.info("End processCGSJCRCreate (" + url + ")," + new Date());
    }

    private void processCGSYCRAnnotation(HttpServletRequest request, JSONObject jsonObj, String cgsUri) throws Exception {
        final String url = getBaseUrl(request) + cgsUri + "/annotations";
        log.info("Begin processCGSYCRAnnotation (" + url + ")," + new Date() + " sequencing batch  )");
        PostMethod postCreate = new PostMethod(url);
        try {
            if (this.login(getHttpsUrl(request))) {
                postCreate.setQueryString(new NameValuePair[]{new NameValuePair("JSON", jsonObj.toString())});
                this.httpClientTemplate.executeMethod(postCreate, new ResponseCallback() {
                    public Object onResponse(int status, HttpMethod httpMethod) throws HttpClientResponseException {
                        if (status != 200) {
                            throw new HttpClientResponseException(status, httpMethod, null);
                        } else {
                            log.info("YCR Annotations updated\n" + url + " successful");
                            return null;
                        }
                    }
                }
                );
            } else {
                log.error("Failed Authentication");
            }
        } catch (Exception e) {
            e.printStackTrace();
            this.sendNotification("LIMS Notification:Update YCR failed:" + url, "Create error while processing " + url + "\nExeception\n" + e + "\nJake will be looking into it.");
            throw e;
        }
        finally {
            postCreate.releaseConnection();
            log.info("released connection for url " + url);
        }
        log.info("End processCGSYCRAnnotation (" + url + ")," + new Date());
    }


    private void processIGVLabspaceBam(HttpServletRequest request, JSONObject jsonObj, String cgsKey) throws Exception {
        String baseUrl = getBaseUrl(request);
        String seqYOKey = jsonObj.getString("YCGSSeqRun") + "_" + jsonObj.getString("YCGSStrain");
        final String url = baseUrl + "/addama-rest/" + this.getSeqRepoName() + "/path/igv/labspace/bam_sam/annotations";
        log.info("Begin processIGVLabspace (" + url + ")," + new Date() + " bam labspace files)");
        PostMethod postCreate = new PostMethod(url);
        JSONObject spaceJson = new JSONObject();

        if (jsonObj.has("cgsbam")) {
            spaceJson.put(cgsKey + "_" + seqYOKey + "_bam",
                    baseUrl + "/igv/" + cgsKey + "_" + seqYOKey + ".bam");
        }
        if (jsonObj.has("cgsbai")) {
            spaceJson.put(cgsKey + "_" + seqYOKey + "_bai",
                    baseUrl + "/igv/" + cgsKey + "_" + seqYOKey + ".bai");
        }
        try {
            if (this.login(getHttpsUrl(request))) {
                postCreate.setQueryString(new NameValuePair[]{new NameValuePair("JSON", spaceJson.toString())});
                this.httpClientTemplate.executeMethod(postCreate, new ResponseCallback() {
                    public Object onResponse(int status, HttpMethod httpMethod) throws HttpClientResponseException {
                        if (status != 200) {
                            throw new HttpClientResponseException(status, httpMethod, null);
                        } else {
                            log.info("posting processIGVLabspace annotations " + url + " successful");
                            return null;
                        }
                    }
                }
                );
            } else {
                log.error("Failed Authentication");
            }
        } catch (Exception e) {
            e.printStackTrace();
            this.sendNotification("LIMS processIGVLabspace:Create Status Error:" + url, "Create error while processing " + url + "\nExeception\n" + e + "\nJake will be looking into it.");
            throw e;
        }
        finally {
            postCreate.releaseConnection();
            log.info("released connection for url " + url);
        }
        log.info("End processing IGVLabspace Bam (" + url + ")," + new Date());
    }

    private void processIGVLabspaceCrossMutation(HttpServletRequest request, JSONObject jsonObj) throws Exception {
        String baseUrl = getBaseUrl(request);
        final String url = baseUrl + "/addama-rest/" + this.getSeqRepoName() + "/path/igv/labspace/mutation/annotations";
        PostMethod postCreate = new PostMethod(url);
        JSONObject spaceJson = new JSONObject();
        String ycrlabel = jsonObj.getString("YCR") + "_" + jsonObj.getString("YCGSKey1").split("_")[0] + "_" + jsonObj.getString("YCGSKey2").split("_")[0];
        log.info("Post GVLabspaceMutation (" + url + ")," + new Date() + " mutation labspace files):" + ycrlabel);
        spaceJson.put(ycrlabel, baseUrl + "/igv/" + ycrlabel + "_merged.mut");
        try {
            if (this.login(getHttpsUrl(request))) {
                postCreate.setQueryString(new NameValuePair[]{new NameValuePair("JSON", spaceJson.toString())});
                this.httpClientTemplate.executeMethod(postCreate, new ResponseCallback() {
                    public Object onResponse(int status, HttpMethod httpMethod) throws HttpClientResponseException {
                        if (status != 200) {
                            throw new HttpClientResponseException(status, httpMethod, null);
                        } else {
                            log.info("posting processIGVLabspaceCross annotations " + url + " successful");
                            return null;
                        }
                    }
                });
            } else {
                log.error("Failed Authentication");
            }
        } catch (Exception e) {
            e.printStackTrace();
            this.sendNotification("LIMS processIGVLabspaceCross:Update Error:" + url, "Create error while processing " + url + "\nExeception\n" + e + "\nJake will be looking into it.");
            throw e;
        }
        finally {
            postCreate.releaseConnection();
            log.info("released connection for url " + url);
        }
        log.info("End processing processIGVLabspaceMutation (" + url + ")," + new Date());
    }

    private void processIGVLabspaceMutation(HttpServletRequest request, JSONObject jsonObj, String cgsKey) throws Exception {
        String baseUrl = getBaseUrl(request);
        String seqYOKey = jsonObj.getString("YCGSSeqRun") + "_" + jsonObj.getString("YCGSStrain");
        final String url = baseUrl + "/addama-rest/" + this.getSeqRepoName() + "/path/igv/labspace/mutation/annotations";
        log.info("Begin processIGVLabspaceMutation (" + url + ")," + new Date() + " mutation labspace files)");
        PostMethod postCreate = new PostMethod(url);
        JSONObject spaceJson = new JSONObject();
        String cgslabel = cgsKey + "_" + seqYOKey + "_";
        if (jsonObj.has(seqYOKey + "_RA")) {
            spaceJson.put(cgsKey + "_" + seqYOKey + "_RA",
                    baseUrl + "/igv/" + cgsKey + "_" + seqYOKey + "_" + jsonObj.getString(seqYOKey + "_RA"));
        }
        if (jsonObj.has(seqYOKey + "_RA_changes_classification")) {
            spaceJson.put(cgsKey + "_" + seqYOKey + "_RA_changes_classification",
                    baseUrl + "/igv/" + cgsKey + "_" + seqYOKey + "_" + "RA_changes_classification.mut");
        }

        if (jsonObj.has(seqYOKey + "_DN")) {
            spaceJson.put(cgsKey + "_" + seqYOKey + "_DN",
                    baseUrl + "/igv/" + cgsKey + "_" + seqYOKey + "_" + jsonObj.getString(seqYOKey + "_DN"));
        }
        if (jsonObj.has(seqYOKey + "_IA")) {
            spaceJson.put(cgsKey + "_" + seqYOKey + "_IA",
                    baseUrl + "/igv/" + cgsKey + "_" + seqYOKey + "_" + jsonObj.getString(seqYOKey + "_IA"));
        }
        if (jsonObj.has(seqYOKey + "_DN_contigs")) {
            spaceJson.put(cgsKey + "_" + seqYOKey + "_DN_contigs",
                    baseUrl + "/igv/" + cgsKey + "_" + seqYOKey + "_" + jsonObj.getString(seqYOKey + "_DN_contigs"));
        }
        if (jsonObj.has(seqYOKey + "_RA_gaps")) {
            spaceJson.put(cgsKey + "_" + seqYOKey + "_RA_gaps",
                    baseUrl + "/igv/" + cgsKey + "_" + seqYOKey + "_" + jsonObj.getString(seqYOKey + "_RA_gaps"));
        }
        if (jsonObj.has(seqYOKey + "_DN_gaps")) {
            spaceJson.put(cgsKey + "_" + seqYOKey + "_DN_gaps",
                    baseUrl + "/igv/" + cgsKey + "_" + seqYOKey + "_" + jsonObj.getString(seqYOKey + "_DN_gaps"));
        }
        try {
            if (this.login(getHttpsUrl(request))) {
                postCreate.setQueryString(new NameValuePair[]{new NameValuePair("JSON", spaceJson.toString())});
                this.httpClientTemplate.executeMethod(postCreate, new ResponseCallback() {
                    public Object onResponse(int status, HttpMethod httpMethod) throws HttpClientResponseException {
                        if (status != 200) {
                            throw new HttpClientResponseException(status, httpMethod, null);
                        } else {
                            log.info("posting processIGVLabspace annotations " + url + " successful");
                            return null;
                        }
                    }
                }
                );
            } else {
                log.error("Failed Authentication");
            }
        } catch (Exception e) {
            e.printStackTrace();
            this.sendNotification("LIMS processIGVLabspace:Create Status Error:" + url, "Create error while processing " + url + "\nExeception\n" + e + "\nJake will be looking into it.");
            throw e;
        }
        finally {
            postCreate.releaseConnection();
            log.info("released connection for url " + url);
        }
        log.info("End processing processIGVLabspaceMutation (" + url + ")," + new Date());
    }

    private void addMultipartParam(JSONObject jsonObject, NativeWebRequest nativeWebRequest) throws JSONException {
        Object nativeRequest = nativeWebRequest.getNativeRequest();
        if (nativeRequest instanceof DefaultMultipartHttpServletRequest) {
            DefaultMultipartHttpServletRequest multipartRequest = (DefaultMultipartHttpServletRequest) nativeRequest;
            filesByName.putAll(multipartRequest.getFileMap());
            Iterator it = this.filesByName.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry mp = (Map.Entry) it.next();
                MultipartFile file = (MultipartFile) mp.getValue();
                log.info("uploaded filename:" + file.getOriginalFilename());
                jsonObject.put(mp.getKey().toString(), file.getOriginalFilename());
            }
        }
    }

    private void forwardAttachments(NativeWebRequest nativeWebRequest, EntityEnclosingMethod method, String cgsKey, String seqKey, String sample, String fc, String lane) throws IOException {
        Object nativeRequest = nativeWebRequest.getNativeRequest();
        if (nativeRequest instanceof MultipartHttpServletRequest) {
            MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) nativeRequest;
            Map<String, MultipartFile> filesByName = multipartRequest.getFileMap();
            //log.info("forwardAttachments():" + filesByName.size());
            ArrayList<Part> parts = new ArrayList<Part>();
            for (Map.Entry<String, MultipartFile> entry : filesByName.entrySet()) {
                //log.info("from uploaded stream:" + entry.getValue().getOriginalFilename());
                String filename = entry.getValue().getOriginalFilename();
                try {
                    parts.add(new FilePart(entry.getValue().getOriginalFilename(), new MultipartFilePartSource(entry.getValue())));
                    if (filename.indexOf("RA_changes_classification") != -1) {
                        CGSChromRefMap.initChromMaps();
                        InputStream inputStream = entry.getValue().getInputStream();
                        Reader r = new InputStreamReader(inputStream, "US-ASCII");
                        BufferedReader br = new BufferedReader(r);
                        try {
                            String line;
                            while ((line = br.readLine()) != null) {
                                //    protected void processCGSSNPView(String cgskey, String seqKey, String sample, String fc, String lane, String line) {
                                processCGSChangesClassifications(cgsKey, seqKey, sample, fc, lane, line);
                            }
                        } finally {
                            br.close();
                        }

                        inputStream.close();
                    }

                } catch (Exception e) {
                    log.error("Exception at reading upload file stream");
                    this.sendNotification("LIMS Notification:Multiplex-Omics populating Error:" + cgsKey, "SlimSeq error processing " + cgsKey + "\nWith exception\n" + e);
                    throw new IOException();
                }
            }
            if (!parts.isEmpty()) {
                method.setRequestEntity(new MultipartRequestEntity(parts.toArray(new Part[parts.size()]), method.getParams()));
            }
        }
    }

    private void reportPloidy(HttpServletRequest request, JSONObject json) throws Exception {
        log.info("Handling CGS jcr update processYOPloidy");
        final String addamaUrl = "/addama-rest/dudley-sample/path/datasource/YO/" + json.getString("YCGSStrain").replaceAll("YO", "YO%20") + "/annotations";
        PostMethod post = new PostMethod(getBaseUrl(request) + addamaUrl);
        try {
            JSONObject annotationJson = new JSONObject();
            annotationJson.put("ploidy", json.getString("ploidy"));
            annotationJson.put("ploidyNotes", json.getString("YCGSKey") + " -" + json.getString("ploidyNotes"));
            annotationJson.put("ploidyDetermination", "Sequencing");
            post.setQueryString(new NameValuePair[]{new NameValuePair("JSON", annotationJson.toString())});
            this.httpClientTemplate.executeMethod(post, new ResponseCallback() {
                public Object onResponse(int status, HttpMethod httpMethod) throws HttpClientResponseException {
                    if (status != 200) {
                        log.error("processYOPloidy post request failed with status:" + status);
                        throw new HttpClientResponseException(status, httpMethod, null);
                    } else {
                        log.info("Posted OKAY request on:" + addamaUrl);
                    }
                    return null;
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
            log.error("Error exception:" + e + "\nURL:" + addamaUrl);
            this.sendNotification("LIMS Notification:Annotation Error:" + addamaUrl, "Create error while processing " + addamaUrl + "\nExeception\n" + e + "\nJake will be looking into it.");
        }
        finally {
            post.releaseConnection();
            log.info("released connection for url " + addamaUrl);
        }
        log.info("End processYOPloidy (" + addamaUrl + ")");
    }


    private void processCGSJCRAnnotation(HttpServletRequest request, int count) throws Exception {
        log.info("Handling CGS jcr update annotation");
        final String addamaUrl = "/addama-rest/dudley-sample/path/datasource/YCGS/annotations";
        PostMethod post = new PostMethod(getBaseUrl(request) + addamaUrl);
        try {
            JSONObject annotationJson = new JSONObject();
            String nextSeqVal = count + "";
            annotationJson.put("YCGSCount", nextSeqVal);
            annotationJson.put("YCGSMaxNum", nextSeqVal);

            post.setQueryString(new NameValuePair[]{new NameValuePair("JSON", annotationJson.toString())});
            this.httpClientTemplate.executeMethod(post, new ResponseCallback() {
                public Object onResponse(int status, HttpMethod httpMethod) throws HttpClientResponseException {
                    if (status != 200) {
                        log.error("processSequencingJCRAnnotation post request failed with status:" + status);
                        throw new HttpClientResponseException(status, httpMethod, null);
                    } else {
                        log.info("Posted OKAY request on:" + addamaUrl);
                    }
                    return null;
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
            log.error("Error exception:" + e + "\nURL:" + addamaUrl);
            this.sendNotification("LIMS Notification:Annotation Error:" + addamaUrl, "Create error while processing " + addamaUrl + "\nExeception\n" + e + "\nJake will be looking into it.");
        }
        finally {
            post.releaseConnection();
            log.info("released connection for url " + addamaUrl);
        }
        log.info("End processCGSJCRAnnotation (" + addamaUrl + ")");
    }

    /*
       * Public Methods
       */
    public boolean login(String baseUrl) {
        try {
            StringBuilder queryString = new StringBuilder();
            log.info("login with username:password " + this.getAddamaDudleyUser() + ":" + this.getAddamaDudleyPassword());
            queryString.append("username=").append(this.getAddamaDudleyUser());
            queryString.append("&password=").append(this.getAddamaDudleyPassword());

            Protocol.registerProtocol("https", new Protocol("https", new EasySSLProtocolSocketFactory(), 8443));
            PostMethod post = new PostMethod(baseUrl + "/addama-login/login");
            post.setQueryString(queryString.toString());

            return (Boolean) this.httpClientTemplate.executeMethod(post, new IsExpectedStatusCodeResponseCallback(200));
        } catch (Exception e) {
            log.error("login(" + this.getAddamaDudleyUser() + ")", e);
            return false;
        }
    }


    protected Collection getStrainRadtagBc(String seqkey) {
        log.info("Begin getStrainRadtagBc (" + seqkey + ")");
        return jdbcTemplate.query("select strains, barcode, key_meta from omics_samples where omics_key = '" + seqkey + "'", new StrainRadtagMapper());

    }

    private static final class StrainRadtagMapper implements RowMapper {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
            StrainRadtag rt = new StrainRadtag();
            rt.setStrainKey(rs.getString("strains"));
            String barcode = rs.getString("barcode");
            if (barcode != null) {
                rt.setRadtagBc(barcode);
            } else {
                rt.setRadtagBc("not_available");
            }
            rt.setFlowcell_lane(rs.getString("key_meta"));
            return rt;
        }
    }

    protected void sendNotification(String subject, String message) {
        this.templateMessage.setSubject(subject);
        SimpleMailMessage smsg = new SimpleMailMessage(this.templateMessage);
        smsg.setText(message);
        this.mailSender.send(smsg);
    }

    /*
INSERT INTO `omics`.`omics_samples` (omics_key,omics_type, submitted_date,project,status,status_date,strains,key_meta,comments)
VALUES ('YSEQ25','NGS',{d '2010-12-18'},'NV NGS_Solid','submitted',{d '2010-08-01'},'YO_502','','')
commit
*/
    protected void processCGSOmicsView(String cgskey, String type, String sample, String project, String status, String key_meta, String comments, String op) {
        log.info("Begin processCGSOmicsView (" + cgskey + " " + new Date() + " sequencing batch " + sample + " )");

        final String key = cgskey;
        final String ftype = type;
        final String si = sample.replaceAll("YO", "YO_");
        final String fproject = project;
        final String fstatus = status;
        final String fmeta = key_meta;
        final String fcomments = comments;

        final java.sql.Date fdate = new java.sql.Date(System.currentTimeMillis());
        PreparedStatementSetter insertPS = new PreparedStatementSetter() {
            public void setValues(PreparedStatement preparedStatement) throws SQLException {
                preparedStatement.setString(1, key);
                preparedStatement.setString(2, ftype);
                preparedStatement.setDate(3, fdate);
                preparedStatement.setString(4, fproject);
                preparedStatement.setString(5, fstatus);
                preparedStatement.setDate(6, fdate);
                preparedStatement.setString(7, si);
                preparedStatement.setString(8, fmeta);
                preparedStatement.setString(9, fcomments);

            }
        };
        if (op.equals("INSERT")) {
            String insertSql = "INSERT INTO omics_samples (omics_key,omics_type,submitted_date,project,status,status_date,strains,key_meta,comments) VALUES (?,?,?,?,?,?,?,?,?)";
            jdbcTemplate.update(insertSql, insertPS);
        } else {
            if (sample.equals("*")) {//indicates that it is multiplex, where one sample (YMULTI) represents groups of strains
                String updateSql = "UPDATE omics_samples set status=?, status_date=?, key_meta=? where omics_key = ?";
                jdbcTemplate.update(updateSql, new Object[]{fstatus, fdate, fmeta, key});
            } else {
                String updateSql = "UPDATE omics_samples set status=?, status_date=?, key_meta=? where omics_key = ? and strains = ?";
                jdbcTemplate.update(updateSql, new Object[]{fstatus, fdate, fmeta, key, sample});
            }
        }
        log.info("Done processCGSOmicsView (" + cgskey + " " + new Date() + " sequencing batch " + sample + " )");
    }

    /*
INSERT INTO `omics`.`cgs_snp`
(cgs_key,seq_key,strain_key,flowcell,lane,chrom,
ref_start,ref_end,
ref_name,ref_seq,mod_seq,
mod_start,mod_end,score,comments)
VALUES ('s' ,'s' ,'s' ,'s' ,'s' ,'s',0,0,'s','s','s',0,0,0,'s');
    YCGS1	YSEQ4	YO502	FC42C2EAAXX	4	chr01.fa	139	139	NC_001133.7	T	A	139	139	67.0
YCGS1	YSEQ4	YO502	FC42C2EAAXX	4	chr01.fa	140	140	NC_001133.7	C	T	140	140	58.0
YO502	100211_HWI-EAS256_FC42C2EAAXX	S288C_chr01.fa	139	139	T	A	139	139	67
     */
    protected void processCGSChangesClassifications(String cgskey, String seqKey, String sample, String fc, String lane, String line) throws SQLException {
        try {
            String[] tk = line.split("\t");
            log.info("Begin processCGSSNPView (" + cgskey + " " + new Date() + " sample " + sample + "\n" + line + "\n)");// + " line:" + line + " )");
            if (tk.length >= 10) {
                final String chrom = tk[0];
                final int ref_start = Integer.parseInt(tk[1]);
                final int ref_end = Integer.parseInt(tk[2]);
                final String ref_name = CGSChromRefMap.chromRefMap.get(chrom);
                final String ref_seq = tk[3];
                final String mod_seq = tk[4];
                final int mod_start = Integer.parseInt(tk[5]);
                final int mod_end = Integer.parseInt(tk[6]);
                final double score = Double.parseDouble(tk[7]);
                final String gene = tk[8];
                final String ctype = tk[9];

                String rep = "";
                if (tk.length >= 11)
                    rep = tk[10];
                final String repetitve_element = rep;
                String alias = "";
                if (tk.length >= 12)
                    alias = tk[11];
                final String geneAlias = alias;
                String pos = "";
                if (tk.length >= 13)
                    alias = tk[12];
                final String position = alias;
                String sub = "";
                if (tk.length >= 14)
                    sub = tk[13];
                final String substitution = sub;
                String annotation = "";
                if (tk.length >= 15)
                    annotation = tk[13] + " " + tk[14];
                final String annotations = annotation;
                //final String comments = tk.nextToken();
                final String key = cgskey;
                final String skey = seqKey;
                sample = sample.replaceAll("YO", "YO_");
                final String si = sample.replaceAll("YPG", "YPG_");

                final String ffc = fc;
                final String flane = lane;
                //final java.sql.Date fdate = new java.sql.Date(System.currentTimeMillis());
                PreparedStatementSetter insertPS = new PreparedStatementSetter() {
                    public void setValues(PreparedStatement preparedStatement) throws SQLException {
                        preparedStatement.setString(1, key);
                        preparedStatement.setString(2, skey);
                        preparedStatement.setString(3, si);
                        preparedStatement.setString(4, ffc);
                        preparedStatement.setString(5, flane);
                        preparedStatement.setString(6, chrom);
                        preparedStatement.setInt(7, ref_start);
                        preparedStatement.setInt(8, ref_end);
                        preparedStatement.setString(9, ref_name);
                        preparedStatement.setString(10, ref_seq);
                        preparedStatement.setString(11, mod_seq);
                        preparedStatement.setInt(12, mod_start);
                        preparedStatement.setInt(13, mod_end);
                        preparedStatement.setDouble(14, score);
                        preparedStatement.setString(15, gene);
                        preparedStatement.setString(16, ctype);
                        preparedStatement.setString(17, repetitve_element);
                        preparedStatement.setString(18, geneAlias);
                        preparedStatement.setString(19, position);
                        preparedStatement.setString(20, substitution);
                        preparedStatement.setString(21, annotations);

                    }
                };
                /*
               ctype varchar(30),
      repeat_element varchar(50),
      gene_alias varchar(50),
      position varchar(50),
      substitution varchar(100),
      annotations varchar(255),
      comments varchar(255)
                */
                String insertSql = "INSERT INTO cgs_changes_classifications (cgs_key,seq_key,strain_key,flowcell,lane,chrom,ref_start,ref_end,ref_name,ref_seq,mod_seq,mod_start,mod_end,score,gene,ctype,repeat_element,gene_alias,position,substitution,annotations) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
                jdbcTemplate.update(insertSql, insertPS);
            } else {
                log.warn("Line in file has less than 13 columns (" + tk.length + "\n" + line + " )");
                msgBuffer.append("not enough (<10) tokens->" + tk.length + " " + line + "\n");
            }

            log.info("Done processCGSSNPView (" + cgskey + " " + new Date() + " sample " + sample + " )");

        } catch (Exception e) {
            log.warn("Exception in line:" + line + "\n" + e);
            msgBuffer.append("Exception in line:" + line + "error:" + e);
            e.printStackTrace();
            throw new SQLException();
        }

    }


}
